module SimpleFormHelper

  def titulo(accion, nombre, options = {})  
    unless options[:header] == false
      header = options[:header] || "#{accion} #{nombre}"
      aux =  "<span class='titulo_centrado'>"
      aux << "  <h3>#{header}</h3>" 
      unless options[:back_link] == false
        aux << "  <span>#{link_to_function 'Volver', 'history.back()' } </span>"
      end
      aux << "</span>"
    else
      ''
    end
  end
  
  def edit_form_for(*args, &proc)
    name, object, options = form_parameters(*args)
    options[:html] ||= {}
    options[:html].merge! :method => 'put'
    options[:form_action] = :edit
    options[:url] = options_url(name, object, :update, options)

    concat( titulo("Editando", name, options), proc.binding )
    simple_form_for(name, object, options, &proc)
  end
  
  def new_form_for(*args, &proc)
    name, object, options = form_parameters(*args)
    options[:method] = 'post'
    options[:form_action] = :new
    options[:url] = options_url(name, object, :create, options)
    
    concat( titulo("Creando", name, options), proc.binding)
    simple_form_for(name, object, options, &proc)
  end
  
  def show_form_for(*args, &proc)
    name, object, options = form_parameters(*args)
    concat( titulo('Datos', name, options), proc.binding)
    simple_form_for(name, object, options.merge(:read_only => true), &proc)
  end
  
  def inline_form_for(name, object, options, &proc)
    if ['new', 'create'].include?(controller.action_name)
      new_form_for(name, object, options, &proc)
    elsif ['edit', 'update'].include?(controller.action_name)
      edit_form_for(name, object, options, &proc)
    end
  end
  
  def simple_form_for(*args, &proc)
    name, object, options = form_parameters(*args)
    concat titulo(nil, name, options), proc.binding if options[:header]
    concat("<div class='formulario'>", proc.binding)
    options[:html] ||= {}
    options.merge! :builder => MyFormBuilder
    if options[:remote]
      form_remote_for(name, object, options, &proc)
    else
      form_for(name, object, options, &proc)
    end
    concat("</div>", proc.binding)
  end

  def label_tag(label, options = {})
    content_tag(:label, label, options)
  end
  
  def nav_label(method, options)
    options[:label] || case method
      when Symbol, String
        method.to_s
      when Array
        method.first.to_s
    end.humanize
  end

  def nav_field(model, method, options = {})
    label = nav_label(method, options)
    content_tag(:label, label + ':' + content_tag(:span, multi_send(instance_variable_get("@#{model}"), method)))
  end
  
  def nav_text_field(model, method, options = {})
    label = nav_label(method, options)
    content_tag(:label, label + ':' + text_field(model, method, options.merge(:class => 'entrada')))
  end

  def nav_text_area(model, method, options = {})
    label = nav_label(method, options)
    content_tag(:label, label + ':' + text_area(model, method, options.merge(:class => 'entrada')))
  end

  def nav_check_box(model, method, options = {}, checked = "1", unchecked = "0")
    label = nav_label(method, options)
    content_tag(:label, label + ':' + check_box(model, method, options.merge(:class => 'entrada'), checked, unchecked))
  end

  def nav_select(model, method, choices, options = {}, html_options = {})
    label = nav_label(method, options)
    content_tag(:label, label + ':' + select(model, method, choices, options, html_options.merge(:class => 'entrada')))
  end
  
  def nav_date_select(model, method, options = {})
    label = nav_label(method, options)
    options = options.dup.merge(:order => [:day, :month, :year])
    dselect = date_select(model, method, options)
    content_tag(:label, label + ':' + content_tag(:span, dselect, :class => 'date_entrada'))
  end
  
  def nav_columns(cols, &block)
    cls = cols > 1 ? "entradas_#{cols}" : 'entradas'
    concat "<div class='#{cls}'>", block.binding
    yield
    concat "</div>", block.binding
    concat "<br style='clear:both'/>", block.binding
  end

  def field_label_tag(label, span, options = {})
    content_tag(:label, label + content_tag(:span, span, options[:span]), options[:label])
  end
  
  def link_to_cancel(txt = 'Cancelar', options = {})
    options.merge! :action => 'index'
    if options.delete(:remote)
      model = options[:model]
      cancel_link_id = model ? "#{model}_cancel_link" : 'cancel_link'
      span_options = {}
      span_options[:id] = options[:hidden] ? cancel_link_id : nil
      span_options[:style] = 'display:none' if options[:hidden]
      link = link_to_cancel_js(txt, model, cancel_link_id)
      @template.content_tag(:span, link, span_options)
    else
      @template.link_to(txt, options, {:class => 'cancelar'}) 
    end
  end
  
  def link_to_cancel_js(txt, model, cancel_link_id)
    js = update_page do |page|
      page["#{model}_new_link"].show
      page["#{model}_spinner"].hide
      page[cancel_link_id].hide
      page["#{model}_inline_form"].visual_effect :blind_up, :duration => 0.2
    end
    @template.link_to_function(txt, js)
  end
 
  def button_text(buttons, defa_txt, action_id, form_action)
    text = defa_txt
    if buttons
      accept_button = buttons.find {|button| button[action_id] && !button[:if] || button[action_id] && button[:if] == form_action}
      text = accept_button[action_id] if accept_button
    end
    text
  end
    
  class MyFormBuilder < ActionView::Helpers::FormBuilder
    attr_reader :options
    
    def initialize(object_name, object, template, options, proc)
      super
      @controller_name  = options[:controller_name] || @template.controller.controller_name || @object_name.to_s.pluralize
      @controller_class_name = "#{@controller_name}_controller".classify
      @controller_class = Object::const_get(@controller_class_name)
      @options[:show_nil] = true if @options[:show_nil].nil?
      @form_columns = []
    end
  
    (field_helpers - %w(check_box radio_button hidden_field)).each do |selector|
      define_method(selector) do |*params|
        fld = params[0]
        options = params[1] || {}
        return '' if no_show?(fld, options)
        if read_only(options)
          field(fld, options)
        else
          options = clase(options)
          wrapper(fld, label(fld, options)+ ": " + super(fld, options), options)
        end
      end
    end
    
    def hidden_field(fld, options = {})
      return '' if no_show?(fld, options)
      super(fld, options)
    end
    
    def check_box(fld, options = {})
      return '' if no_show?(fld, options)
      if read_only(options)
        field(fld, options)
      else
        options = clase(options, 'check_box')
        wrapper(fld, super(fld, options) + label(fld, options), options.merge(:check_box => true))
      end
    end
    
    def select(fld, choices, options = {}, html_options = {})
      return '' if no_show?(fld, options)
      if read_only(options)
        field(fld, options)
      else
        options = clase(options)
        html_options = clase(html_options.merge(:columns => options[:columns]))
        wrapper(fld, label(fld, options)+ ": " + super(fld, choices, options, html_options), options)
      end
    end

    def date_select(fld, options = {})
      return '' if no_show?(fld, options)
      if read_only(options)
        field(fld, options)
      else
        options = {:order => [:day, :month, :year]}.merge(clase(options))
        wrapper(fld, label(fld, options) + ': ' + 
                @template.content_tag(:span, super, :class => 'date_entrada'),
                options)
      end
    end
 
    def datetime_select(fld, options = {})
      return '' if no_show?(fld, options)
      if read_only(options)
        field(fld, options)
      else
        options = clase(options)
        wrapper(fld, label(fld, options) + ': ' + 
                @template.content_tag(:span, super, :class => 'date_entrada'),
                options)
      end
    end
    
    def select_time(fld, options = {})
      return '' if no_show?(fld, options)
      if read_only(options)
        field(fld, options.merge(:read_only => true))
      else
        options = clase(options).merge(:prefix => fld)
        wrapper(fld, label(fld, options) + ': ' + 
                      @template.content_tag(:span, @template.select_time(fld_value(fld, options), options), 
                                            :class => 'date_entrada'),
                      options)
      end
    end
    
    def auto_complete(object, method, options = {})
      return '' if no_show?(object, options)
      if read_only(options)
        field(object, options)
      else
        hidden_field_id = "#{@object_name}_#{object}_id"
        spinner_id = "auto_complete_#{object}_spinner"
        method_name = method.is_a?(Array) ? method.join('_and_') : method
        @template.instance_variable_set("@#{object}", @object.send(object))
        rtn = wrapper(object, label(method, options) + ': ' +
                               @template.text_field_with_auto_complete(object, method_name, {:class => :entrada}, :indicator => spinner_id, 
                                           :after_update_element => "function(e, v) {$('#{hidden_field_id}').value = v.id.replace('#{object}_auto_complete_id_', '');}"),
                               options)
        rtn << @template.spinner(:id => spinner_id)
        rtn << hidden_field(:"#{object}_id")
      end
    end
    
    def columns(cols = 1, &block)
      @form_columns << cols
      @template.nav_columns(cols, &block)
      @form_columns.pop
    end
    
    def field(fld, options = {})
      return '' if no_show?(fld, options)
      if fld.to_s =~ /([\w]+)_id$/
        fld = $1.to_sym
      end
      opts = options[:highlight] ? {:span => {:class => 'highlight'}} : {}
      opts.delete(:columns)
      opts[:label] = {:id => 'label_' + @object_name.to_s + fld.to_s}
      opts[:label].merge! :style => 'display:none' if options[:hidden]
      @template.field_label_tag(label(fld, options) + ': ', fld_value(fld, options).to_s, opts)
    end

    def accept(options)
      id = @object_name ? "#{@object_name}_accept_button" : "accept_button"
      txt = @template.button_text(@controller_class.buttons, 'Aceptar', :accept, @options[:form_action])
      @template.submit_tag(txt, :class => 'aceptar', :id => id, :onclick => "$('#{id}').disabled = true")
    end
    
    def cancel(options)
      txt = @template.button_text(@controller_class.buttons, 'Cancelar', :cancel, @options[:form_action])
      @template.link_to_cancel(txt, options.merge(:model => @object_name))
    end
    
    def delete(options)
      options.merge! :action => 'destroy', :id => @object 
      txt = @template.button_text(@controller_class.buttons, 'Eliminar', :accept, :delete)
      if options.delete(:remote)
        model = @object.class.to_s.underscore
        @template.link_to_remote(txt, @template.toggle_spinner("#{model}_form_spinner", :url => options, :method => :delete),
                                 :class => 'borrar')
      else
        @template.link_to(txt, options, :method => :delete, :class => 'borrar', :confirm => 'Está seguro de que quiere borrar el registro?')
      end
    end
    
    def actions(options = {})
      options.merge! :controller => @controller_name
      if parent = @options[:parent]
        options.merge! "#{parent}_id".to_sym => @template.instance_variable_get("@parent").id
      end
      option_cancel = options.delete(:cancel)
      option_delete = options.delete(:delete)
      acc = ""
      acc << accept(options)
      acc << " &nbsp; "
      acc << cancel(options) unless option_cancel == false
      if options[:remote]
        acc << " &nbsp; " + @template.spinner(:id => "#{@object_name}_form_spinner")
      end
      acc << delete(options) unless option_delete == false
      acc
    end
    
    private

      def fld_value(fld, options = {})
        rtn = multi_send(@object, fld)
        rtn = rtn.strftime('%H:%M') if rtn && options[:format] == :hour && options[:read_only]
        rtn = rtn ? 'SI' : 'NO' if rtn == true || rtn == false
        rtn
      end

      # Permitir que cuando se muestre un campo, si es nulo, no se lo muetre (:show_nil)
      def no_show?(fld, options)
        fld_value(fld).nil? && read_only(options) && @options[:show_nil] == false
      end

      def clase(options, asumir='entrada')
        options[:class] = options[:highlight] ? 'highlight' : asumir
        options[:class] << columns_class(options)
        options
      end
      
      def columns_class(options)
        options[:columns] ? " entradas_#{options[:columns]}_de_#{@form_columns.last}" : ''
      end
      
      def label(fld, options)
        options[:label] || fld.to_s.humanize
      end

      def wrapper(fld, text, options)
        id = @object.class.to_s.underscore + '_' + fld.to_s
        opts = {:id => "label_#{id}"}
        opts.merge! :style => 'display:none;' if options[:hidden]
        opts[:class] = 'check_box' if options[:check_box]
        opts[:class] = opts[:class].to_s + columns_class(options)
        spnr = options[:spinner] ? @template.spinner("#{id}_spinner") : ''
        @template.content_tag(:label, text + spnr, opts)
      end
      
      def read_only(options)
        @options[:read_only] || options[:read_only]
      end
  end
  
  private 

    def options_url(name, object, action_name, options)
      return options[:url] if options[:url]
      controller_name = options[:controller_name] || get_controller_name
      options[:url] = {:controller => controller_name, :action => action_name}
      options[:url][:id] = object.id if action_name != :create
      if parent = options[:parent]
        parent_sym = "#{parent}_id".to_sym
        options[:url][parent_sym] = instance_variable_get('@' + parent.to_s)
      end
      options[:url]
    end
  
end

ActionView::Base.send :include, SimpleFormHelper
